package it.marcelate.rcp.jdtutil.util;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.Assignment.Operator;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;

public class FindAndReplaceMemberSupportImpl1 implements FindAndReplaceMemberSupport {
  
  private String pattern;
  
  public FindAndReplaceMemberSupportImpl1(String pattern) {
    this.pattern = pattern;
  }

  public class StringPatternMethodFinder extends ASTVisitor implements MemberFinder {

    private List<BodyDeclaration> bodyDeclarations = new ArrayList<BodyDeclaration>();
    
    private String pattern;

    public StringPatternMethodFinder(String pattern, CompilationUnit cuNode) throws JavaModelException {
      this.pattern = pattern;
      cuNode.accept(this);
    }

    public boolean visit(MethodDeclaration node) {
      if (node.getBody().toString().indexOf(pattern) > -1) {
        bodyDeclarations.add(node);
      }
      return false;
    }

    @Override
    public List<BodyDeclaration> getBodyDeclarations() {
      return bodyDeclarations;
    }

  }
  
  /* (non-Javadoc)
   * @see it.marcelate.rcp.jdtutil.util.FindAndReplaceMemberSupport#getMemberFinder(org.eclipse.jdt.core.dom.CompilationUnit)
   */
  public MemberFinder getMemberFinder(CompilationUnit cu) throws JavaModelException {
    return new StringPatternMethodFinder(pattern, cu);
  }
  
  /* (non-Javadoc)
   * @see it.marcelate.rcp.jdtutil.util.FindAndReplaceMemberSupport#process(org.eclipse.jdt.core.dom.BodyDeclaration, org.eclipse.jdt.core.dom.AST, org.eclipse.jdt.core.dom.rewrite.ASTRewrite)
   */
  public void process(BodyDeclaration matchingMember, AST ast, ASTRewrite rewriter) {
    MethodDeclaration matchingMethod = (MethodDeclaration)matchingMember;
    
    System.out.println("Found method: " + matchingMethod.getName() + " position: " +
    matchingMethod.getStartPosition() + ":" + 
    matchingMethod.getLength()
    + " body: " + matchingMethod.getBody()
    );
    
    String bs = matchingMethod.getBody().toString();
    String fieldType = bs.substring(bs.indexOf('(') + 1, bs.indexOf(')'));
    int ip = bs.indexOf('"');
    String fieldName = bs.substring(ip + 1, bs.indexOf('"', ip + 1));
    String setMethodName = 's' + matchingMethod.getName().toString().substring(1);
    System.out.println("pt="+fieldType+" pn="+fieldName+" smn="+setMethodName);

    int insertIndex = 0;
    ListRewrite classMembers = rewriter.getListRewrite(matchingMethod.getParent(), TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
    List membersList = classMembers.getRewrittenList();
    for (int im = 0; im < membersList.size(); im++) {
      BodyDeclaration bd = (BodyDeclaration)membersList.get(im);
      if (bd instanceof MethodDeclaration) {
        MethodDeclaration md = (MethodDeclaration)bd;
        if (matchingMethod.getName().equals(md.getName())) {
          insertIndex = im;
        }
      }
    }
    
    rewriter.set(matchingMethod, MethodDeclaration.BODY_PROPERTY, createGetMethodBody(ast, fieldName), null);
    
    classMembers.insertAt(createFieldDeclaration(ast, fieldName, fieldType), insertIndex, null);

    classMembers.insertAt(createSetMethod(ast, setMethodName, fieldType, fieldName), insertIndex + 1, null);
    
  }
  
  private FieldAccess createFieldAccess (AST ast, String fieldName) {
    FieldAccess fa = ast.newFieldAccess();
    fa.setExpression(ast.newThisExpression());
    fa.setName(ast.newSimpleName(fieldName));
    return fa;
  }
  
  private Block createGetMethodBody(AST ast, String fieldName) {
    ReturnStatement rs = ast.newReturnStatement();
    rs.setExpression(createFieldAccess(ast, fieldName));
    Block block = ast.newBlock();
    block.statements().add(rs);
    return block;
  }
  
  private FieldDeclaration createFieldDeclaration(AST ast, String fieldName, String fieldType) {
    VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
    vd.setName(ast.newSimpleName(fieldName));
    FieldDeclaration fd = ast.newFieldDeclaration(vd);
    fd.setType(ast.newSimpleType(ast.newName(fieldType)));
    fd.modifiers().addAll(ast.newModifiers(Modifier.PRIVATE));
    return fd;
  }
  
  private MethodDeclaration createSetMethod(AST ast, String setMethodName, String fieldType, String fieldName) {
    MethodDeclaration setMethod = ast.newMethodDeclaration();
    setMethod.setName(ast.newSimpleName(setMethodName));
    SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
    svd.setName(ast.newSimpleName("value"));
    svd.setType(ast.newSimpleType(ast.newName(fieldType)));
    setMethod.parameters().add(svd);
    Assignment as = ast.newAssignment();
    as.setOperator(Operator.ASSIGN);
    as.setLeftHandSide(createFieldAccess(ast, fieldName));
    as.setRightHandSide(ast.newSimpleName("value"));
    Block block = ast.newBlock();
    block.statements().add(ast.newExpressionStatement(as));
    setMethod.setBody(block);
    setMethod.modifiers().addAll(ast.newModifiers(Modifier.PUBLIC));
    return setMethod;
  }
  

}
